
#include "rc.h"
#include <string.h>
#include <stdio.h>

#define UART_RC             UART_NUM_1
#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE)

static const char *TAG = "rc";
rc_t rc;

static QueueHandle_t uart_rc_queue;
uint8_t uart_rc_buffer[10];
rc_t rc2;   //注意资源互斥使用！最好加个mutex

void parse_rc(uint8_t *buf, uint16_t len)
{
    if (len < 10 || buf[0] != 0xff || buf[1] != 0x5a) {
        return ;
    }
    uint8_t sum = 0;
    for (int i=2; i<=8; ++i) {
        sum += buf[i];
    }
    if (sum != buf[9]) {
        return ;    //checksum failed
        //TODO: when failed many times. we need reset uart.
    }

    memcpy(&rc, &buf[2], sizeof(rc));
    static int logcnt;
    if (logcnt++ >= 5) 
    {
        logcnt = 0;
        ESP_LOGI(TAG, "ch [%3d %3d %3d %3d]", rc.lx, rc.ly, rc.rx, rc.ry);
    }
    // ESP_LOGI(TAG, "dir[%d %d %d %d]", rc.UP, rc.DOWN, rc.LEFT, rc.RIGHT);
}

static void rc_decode_task(void *pvParameters)
{
    uart_event_t event;
    uint8_t* dtmp = (uint8_t*) malloc(RD_BUF_SIZE);
    for(;;) {
        //Waiting for UART event.
        if(xQueueReceive(uart_rc_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
            switch(event.type) {
                /*We'd better handler data event fast, there would be much more data events than
                other types of events. If we take too much time on data event, the queue might
                be full.*/
                case UART_DATA:
                    // ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                    if (event.size == 10) {
                        uart_read_bytes(UART_RC, uart_rc_buffer, event.size, portMAX_DELAY);
                        parse_rc(uart_rc_buffer, event.size);
                        memcpy(&rc2, &rc, sizeof(rc));
                    }
                    // ESP_LOGI(TAG, "[DATA EVT]:");
                    // uart_write_bytes(UART_RC, (const char*) dtmp, event.size);
                    break;
                case UART_FIFO_OVF:
                    ESP_LOGI(TAG, "hw fifo overflow");
                    // If fifo overflow happened, you should consider adding flow control for your application.
                    // The ISR has already reset the rx FIFO,
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(UART_RC);
                    xQueueReset(uart_rc_queue);
                    break;
                case UART_BUFFER_FULL:
                    ESP_LOGI(TAG, "ring buffer full");
                    // If buffer full happened, you should consider encreasing your buffer size
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(UART_RC);
                    xQueueReset(uart_rc_queue);
                    break;
                case UART_BREAK:
                    ESP_LOGI(TAG, "uart rx break");
                    break;
                case UART_PARITY_ERR:
                    ESP_LOGI(TAG, "uart parity error");
                    break;
                case UART_FRAME_ERR:
                    ESP_LOGI(TAG, "uart frame error");
                    break;
                default:
                    ESP_LOGI(TAG, "uart event type: %d", event.type);
                    break;
            }
        }
    }
    free(dtmp); //we will never go here.
    dtmp = NULL;
    vTaskDelete(NULL);
}


void rc_init(void)
{
    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };

    uart_driver_install(UART_RC, BUF_SIZE * 2, BUF_SIZE * 2, 10, &uart_rc_queue, 0);
    uart_param_config(UART_RC, &uart_config);

    //Set UART log level
    esp_log_level_set(TAG, ESP_LOG_INFO);
    //Set UART pins (using UART0 default pins ie no changes.)
    uart_set_pin(UART_RC, UART_RC_TX_IO, UART_RC_RX_IO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    //Create a task to handler URAT event from ISR
    xTaskCreate(rc_decode_task, "rc_decode_task", 2048, NULL, 12, NULL);
}


// Include MicroPython API.
#include "py/runtime.h"

//添加import rc 相关支持
// This is the function which will be called from Python as rc.
STATIC mp_obj_t rc_channels() {
    // Extract the ints from the micropython input objects.
    // int ly = mp_obj_get_int(g_obj);
    // int rx = mp_obj_get_int(b_obj);
    // int ry = mp_obj_get_int(b_obj);

    mp_obj_t tuple[] = {
        MP_OBJ_NEW_SMALL_INT(rc2.lx),
        MP_OBJ_NEW_SMALL_INT(rc2.ly),
        MP_OBJ_NEW_SMALL_INT(rc2.rx),
        MP_OBJ_NEW_SMALL_INT(rc2.ry),
    };

    // convert to MicroPython object.
    return mp_obj_new_tuple(4, tuple);
}

STATIC mp_obj_t rc_keys() {
    mp_obj_t tuple[] = {
        MP_OBJ_NEW_SMALL_INT(rc2.R1),
        MP_OBJ_NEW_SMALL_INT(rc2.R2),
        MP_OBJ_NEW_SMALL_INT(rc2.R3),
        MP_OBJ_NEW_SMALL_INT(rc2.L1),
        MP_OBJ_NEW_SMALL_INT(rc2.L3),
        MP_OBJ_NEW_SMALL_INT(rc2.BT),
        MP_OBJ_NEW_SMALL_INT(rc2.A),
        MP_OBJ_NEW_SMALL_INT(rc2.B),
        MP_OBJ_NEW_SMALL_INT(rc2.X),
        MP_OBJ_NEW_SMALL_INT(rc2.Y),
        MP_OBJ_NEW_SMALL_INT(rc2.POWER),
        MP_OBJ_NEW_SMALL_INT(rc2.START),
    };

    // convert to MicroPython object.
    return mp_obj_new_tuple(12, tuple);
}

STATIC mp_obj_t rc_dirs() {
    mp_obj_t tuple[] = {
        MP_OBJ_NEW_SMALL_INT(rc2.UP),
        MP_OBJ_NEW_SMALL_INT(rc2.DOWN),
        MP_OBJ_NEW_SMALL_INT(rc2.LEFT),
        MP_OBJ_NEW_SMALL_INT(rc2.RIGHT),
    };

    // convert to MicroPython object.
    return mp_obj_new_tuple(4, tuple);
}

STATIC mp_obj_t rc_A() {
    return MP_OBJ_NEW_SMALL_INT(rc2.A);
}

STATIC mp_obj_t rc_B() {
    return MP_OBJ_NEW_SMALL_INT(rc2.B);
}

// Define a Python reference to the function above.
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_channels_obj, rc_channels);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_keys_obj, rc_keys);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_dirs_obj, rc_dirs);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_A_obj, rc_A);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_B_obj, rc_B);

// Define all properties of the module.
// Table entries are key/value pairs of the attribute name (a string)
// and the MicroPython object reference.
// All identifiers and strings are written as MP_QSTR_xxx and will be
// optimized to word-sized integers by the build system (interned strings).
STATIC const mp_rom_map_elem_t rc_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rc) },
    { MP_ROM_QSTR(MP_QSTR_channels), MP_ROM_PTR(&rc_channels_obj) },
    { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&rc_keys_obj) },
    { MP_ROM_QSTR(MP_QSTR_dirs), MP_ROM_PTR(&rc_dirs_obj) },
    { MP_ROM_QSTR(MP_QSTR_A), MP_ROM_PTR(&rc_A_obj) },
    { MP_ROM_QSTR(MP_QSTR_B), MP_ROM_PTR(&rc_B_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rc_module_globals, rc_module_globals_table);

// Define module object.
const mp_obj_module_t rc_user_cmodule = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&rc_module_globals,
};

// Register the module to make it available in Python.
// Note: the "1" in the third argument means this module is always enabled.
// This "1" can be optionally replaced with a macro like MODULE_CEXAMPLE_ENABLED
// which can then be used to conditionally enable this module.
MP_REGISTER_MODULE(MP_QSTR_rc, rc_user_cmodule, 1);